/**
 * @Copyright © 2015 广州四三九九信息科技有限公司. All Rights Reserved.
*/

package com.jkdroid.maya.log;

import android.content.Context;
import android.util.Log;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/*
 * 生产者-消费者模型的日志记录与发送逻辑类
 */
public class ConcurrentEventLog<Item> {

	private static final String TAG = "event";

	private int mMaxRecordSize; // 事件记录最大条数，超出会自动删除旧的。平均每条占 50B
	private boolean mEnableAutoSend; //是否根据NumToSend自动发送
	private int mNumToSend; //自动发送功能的限定条数
	private ArrayList<Item> mEventLogs; //缓存的log
	
	private static boolean canSend = false;
	private static final Lock mLook = new ReentrantLock();/////定义一个单独的Lock对象
    private static final Condition mSendCon = mLook.newCondition();////将监视器绑定到lock上面
    private static final Condition mRecordCon = mLook.newCondition();
	
	// EventLog.record(mContext, "login", "login_show", "");

	private static final ExecutorService mRecordPool = Executors.newFixedThreadPool(3);
	private static final ExecutorService mSendPool = Executors.newSingleThreadExecutor();
	
	private static final AtomicInteger mRecordedCount = new AtomicInteger(0); //for test
	private static final AtomicInteger mSendCount = new AtomicInteger(0);//for test
	
	private final AbstractEventLogStrategy<Item> mStrategy;

	public ConcurrentEventLog(AbstractEventLogStrategy<Item> strategy) {
		if(strategy == null){
			throw new NullPointerException();
		}
		this.mStrategy = strategy;
		mMaxRecordSize = mStrategy.getMaxRecordSize();
		mEnableAutoSend = mStrategy.isEnableAutoSend();
		mNumToSend = mStrategy.getAutoSendNum();
	}
	
	// 记录事件到文件中。
	public void record(Context context, String eventId, String eventName, String eventValue) {
		mRecordPool.execute(new RecordRunnable(context, eventId, eventName, eventValue));
	}

	// 发送记录在文件中的所有event 事件
	public void send(final Context context) {
		mSendPool.execute(new SendRunnable(context));
	}

	public void clear(Context context) {
		mStrategy.clearAll(context);
	}
	
	private void rollBack(Context context) { // 发送失败后回滚
		ArrayList<Item> bkRecords = mStrategy.getBackupEventLogs(context);
		if(bkRecords == null || bkRecords.isEmpty()){
			return;
		}
		ArrayList<Item> records = mStrategy.getEventLogs(context);
		bkRecords.addAll(records);
		int size = bkRecords.size();
		if(size > mMaxRecordSize){
			bkRecords = new ArrayList<Item>(bkRecords.subList(size - mMaxRecordSize, size));
		}
		mEventLogs = bkRecords;
		mStrategy.saveEventLogs(context, bkRecords);
		mStrategy.clearBackupEventLogs(context);
	}
	
	private void checkRollBack(Context context){
		ArrayList<Item> backupRecords = mStrategy.getBackupEventLogs(context);
		if(!backupRecords.isEmpty()){
			Log.i(TAG, "-- " + "checkRollBack not empty="+backupRecords.size());
			rollBack(context);
		}
	}
	
	private class SendRunnable implements Runnable{

		private final WeakReference<Context> mWeakReferenceContext;
		public SendRunnable(Context context) {
			this.mWeakReferenceContext = new WeakReference<Context>(context);
		}
		
		@Override
		public void run() {
			mLook.lock();
			try{
				while(!canSend){
					mSendCon.await();
				}
				canSend = false;
				Context context = mWeakReferenceContext.get();
				if(context == null){
					return;
				}
				checkRollBack(context);
				ArrayList<Item> records = getRecords(context);
				if(records.isEmpty()){
					Log.i(TAG, "-- " + "send event empty");
					return;
				}
				mStrategy.backupEventlogs(context);
				mStrategy.clearEventLogs(context);
				boolean ret = mStrategy.sendEventLogs(context, records);
				if(ret){
					mSendCount.addAndGet(records.size());
					Log.i(TAG, "-- " + "send event ok! size = "+records.size());
					mStrategy.clearBackupEventLogs(context);
					mEventLogs.clear();
					mEventLogs = null;
				}else{
					rollBack(context);
					Log.i(TAG, "-- " + "send event failed!");
				}
				Log.i(TAG, "-- countRecord="+mRecordedCount+" , countSend="+mSendCount);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}finally{
				try{
					mRecordCon.signal();
				}catch(Exception e){
					
				}
				mLook.unlock();
			}
		}
		
	}
	
	private class RecordRunnable implements Runnable{

		private final String eventId;
		private final String eventName;
		private final String eventValue;
		private final WeakReference<Context> mWeakReferenceContext;
		public RecordRunnable(Context context, String eventId, String eventName, String eventValue) {
			this.mWeakReferenceContext = new WeakReference<Context>(context);
			this.eventId = eventId;
			this.eventName = eventName;
			this.eventValue = eventValue;
		}
		
		@Override
		public void run() {
			mLook.lock();////标记锁
			mRecordedCount.incrementAndGet();
			Log.i(TAG, "-- " + eventId + ", " + eventName + ", " + eventValue);
			try{
				while(canSend){
					mRecordCon.await();
				}
				canSend = true;
				Context context = mWeakReferenceContext.get();
				if(context == null){
					return;
				}
				checkRollBack(context);
				ArrayList<Item> records = getRecords(context);
				int size = records.size();
				Log.i(TAG, "-- " + "send event -record- original size = "+size);
				if(size >= mMaxRecordSize){
					//删除掉超过的记录并多删一条为记录这一次留空间
					records = new ArrayList<Item>(records.subList(size - mMaxRecordSize + 1, size));
				}
				Item eventJson = mStrategy.createEvent(eventId, eventName, eventValue);
				if(eventJson == null){
					return;
				}
				records.add(eventJson);
				mStrategy.saveEventLogs(context, records);
				boolean needSend = mEnableAutoSend & (records.size() % mNumToSend == 0);
				Log.i(TAG, "-- countRecord="+mRecordedCount+" , countSend="+mSendCount+" ,"+needSend);
				if(needSend){
					canSend = true;
					send(context);
				}else{
					canSend = false;
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}finally{
				try{
					mSendCon.signal();
				}catch(Exception e){
					
				}
				mLook.unlock();////标记锁
			}
		}
		
	}

	private ArrayList<Item> getRecords(Context context) {
		if(mEventLogs != null){
			return mEventLogs;
		}else{
			mEventLogs = mStrategy.getEventLogs(context);
		}
		return mEventLogs;
	}
}
